﻿Imports System.ComponentModel
Imports System.Linq.Expressions
Imports System.Text.RegularExpressions

Namespace Assets

    Public Delegate Function ExpressionFunc(ByVal L As Expression, ByVal R As Expression) As BinaryExpression

    Public Class ExpressionFactory

        Protected Shared m_instance As ExpressionFactory = Nothing
        Protected Shared m_expressionpointers As Dictionary(Of String, ExpressionFunc) = New Dictionary(Of String, ExpressionFunc)

        Private Sub New()
            m_expressionpointers.Add("<>", AddressOf BinaryExpression.NotEqual)
            m_expressionpointers.Add("=", AddressOf BinaryExpression.Equal)
            m_expressionpointers.Add(">", AddressOf BinaryExpression.GreaterThan)
            m_expressionpointers.Add("<", AddressOf BinaryExpression.LessThan)
            m_expressionpointers.Add(">=", AddressOf BinaryExpression.GreaterThanOrEqual)
            m_expressionpointers.Add("<=", AddressOf BinaryExpression.LessThanOrEqual)


            'TODO: the following could be modified to work on plugin assemblies. To load customized expressions from a plugin dll
            'Get all classes that implement ICustomExpression.
            Dim customexpressions = GetType(WPFSearchDataGrid).Assembly.GetExportedTypes() _
                                                                .Where(Function(t) Not t.IsInterface) _
                                                                .Where(Function(t) GetType(ICustomExpression).IsAssignableFrom(t))
            'convert dictionary to interface pointer.
            Dim _dictionary As IDictionary(Of String, ExpressionFunc) = DirectCast(m_expressionpointers, IDictionary(Of String, ExpressionFunc))
            'use activator to create instance of object implementing ICustomExpression and add to the expression dictionary.
            For Each ce In customexpressions
                _dictionary.Add(Activator.CreateInstance(ce, New Object() {}).ExpressionPointer())
            Next


        End Sub

        Public Shared Function Instance() As ExpressionFactory
            If IsNothing(m_instance) Then
                m_instance = New ExpressionFactory
            End If
            Return m_instance
        End Function

        Public Function Resolve(mx As Expression, di As IDataItem, pi As Reflection.PropertyInfo) As Expression
            Try
                'NOTE: Pattern constructed using ExpressionPointers[Keys] from construction.
                Dim sb As New Text.StringBuilder("(?<operators>(")
                Dim vars = m_expressionpointers.Select(Function(e) String.Format("(\{0})", e.Key)).ToArray()
                sb.Append(String.Join("|", vars))
                Dim pattern As String = sb.Append("))").ToString
                Dim rgx As Regex = New Regex(pattern)
                Dim [match] As Match = rgx.Match(di.Value)
                Dim o As String = "="
                Dim v As String = di.Value.ToString
                If [match].Success Then
                    o = [match].Groups("operators").Value
                    v = di.Value.ToString.Replace(o, "")
                End If
                Dim tc As TypeConverter = TypeDescriptor.GetConverter(pi.PropertyType)
                Dim va As Object = tc.ConvertFrom(v)
                Dim R As ConstantExpression = Expression.Constant(va, pi.PropertyType)
                Return m_expressionpointers.Item(o)(mx, R)
            Catch ex As InvalidOperationException
                Throw
            End Try
        End Function
    End Class

End Namespace
